#ifndef __UTILS_H
#define __UTILS_H

#include <fstream>
#include <vector>
#include <sstream>
#include <iostream>
#include <random>
#include <ctime>
#include <algorithm>
#include "opencl_wrapper.h"

namespace mslite {

#define CHECK(cl_status) \
if (cl_status != CL_SUCCESS) \
{ \
    std::cout << "Pos:" << __FILE__ << ":" << __LINE__ << ", Function: " << __func__ << "(), Error Code: " << cl_status << std::endl; \
    abort(); \
}

double getAvg(std::vector<double> vals, int removes);

int readSrcCode(const std::string filename, std::string &srcCode);

std::vector<float> matmul(std::vector<float> &lhs, std::vector<float> &rhs, int m, int n, int k);

std::vector<float> ReorderOHWIToI4O4(const std::vector<float> &weight, std::vector<int> shape, int ogroup);

cl_mem createImage2d(cl_context context, cl_channel_type type, int width, int height, void* data, cl_int *status);

double profileTime(cl_event profEvent, bool log);

template <typename Dtype>
std::vector<float> convertToFloat(const std::vector<Dtype>& src) {
    std::vector<float> res(src.size());
    for (int i=0; i<src.size(); i++) {
        res[i] = static_cast<float>(src[i]);
    }
    return std::move(res);
}

template <typename Dtype>
std::vector<Dtype> genData(int len, Dtype min, Dtype max) {
    int seed = time(0);
    // int seed = 0;
    std::default_random_engine generator(seed);
    std::uniform_real_distribution<double> urd(min, max);
    std::vector<Dtype> res(len);
    for (int i=0; i<len; i++) {
        res[i] = static_cast<Dtype>(urd(generator));
    }
    return std::move(res);
}

template <typename Dtype>
std::vector<Dtype> packInRow(std::vector<Dtype> data, std::vector<int> shape, int tileSize) {
    if (tileSize == 0)
        return std::move(data);
    assert(shape.size() == 2 && shape[0] % tileSize == 0 && shape[1] % tileSize == 0);
    std::vector<Dtype> packed(data.size());
    int tileNumInCol = shape[0] / tileSize, tileNumInRow = shape[1] / tileSize;
    int count = 0;
    for (int tileY=0; tileY<tileNumInCol; tileY++) {
        for (int tileX=0; tileX<tileNumInRow; tileX++) {
            for (int y=0; y<tileSize; y++) {
                for (int x=0; x<tileSize; x++) {
                    int globalY = tileY * tileSize + y, globalX = tileX * tileSize + x;
                    packed[count++] = data[globalY*shape[1] + globalX];
                }
            }
        }
    }
    return std::move(packed);
}

template <typename Dtype>
std::vector<Dtype> reorderHWToWH(std::vector<Dtype> data, std::vector<int> shape) {
    std::swap(shape[0], shape[1]);
    std::vector<Dtype> res(data.size());
    for (int y=0; y<shape[0]; y++) {
        for (int x=0; x<shape[1]; x++) {
            res[x*shape[0]+y] = data[y*shape[1] + x];
        }
    }
    return std::move(res);
}

}

#endif